package ptij.ADSL.de.ATUR;

import ptolemy.kernel.*;
import ptolemy.kernel.util.*;
import ptolemy.data.*;
import ptolemy.actor.*;
import ptolemy.data.type.*;
import ptolemy.domains.de.kernel.*;
import ptolemy.data.expr.Parameter;

import ptij.ADSL.common.InitSymbols;

//////////////////////////////////////////////////////////////////////////
//// Initializer
/**

@author Ivan Jeukens
*/
public class Initializer extends DEActor {

    public Initializer(CompositeEntity container, String name) throws 
            NameDuplicationException, IllegalActionException {
        super(container, name);

        reset = new TypedIOPort(this, "reset", true, false);
	reset.setTypeEquals(BaseType.GENERAL);

        timerEvent = new TypedIOPort(this, "timerEvent", true, false);
	timerEvent.setTypeEquals(BaseType.GENERAL);
	
	start = new TypedIOPort(this, "start", false, true);
	start.setTypeEquals(BaseType.BOOLEAN);

	idftMode = new TypedIOPort(this, "idftMode", false, true);
	idftMode.setTypeEquals(BaseType.BOOLEAN);

	dftMode = new TypedIOPort(this, "dftMode", false, true);
	dftMode.setTypeEquals(BaseType.BOOLEAN);
	
	startTimer = new TypedIOPort(this, "startTimer", false, true);
	startTimer.setTypeEquals(BaseType.DOUBLE);
	
	stopTimer = new TypedIOPort(this, "stopTimer", false, true);
	stopTimer.setTypeEquals(BaseType.GENERAL);
         
        inputMode = new Parameter(this, "inputMode", new IntToken(1));
        _inputMode = 1;
        outputMode = new Parameter(this, "outputMode", new IntToken(1));
        _outputMode = 1;
        
        _createInputInterface();
        _createOutputInterface();
    }

    ///////////////////////////////////////////////////////////////////
    ////                     ports and parameters                  ////

    public TypedIOPort reset;
    public TypedIOPort fromATUC[];
    public TypedIOPort timerEvent;
        
    public TypedIOPort start;
    public TypedIOPort toATUC[];
    public TypedIOPort idftMode;
    public TypedIOPort dftMode;
    public TypedIOPort startTimer;
    public TypedIOPort stopTimer;
    
    public Parameter inputMode;
    public Parameter outputMode;

    ///////////////////////////////////////////////////////////////////
    ////                         public methods                    ////

    public void attributeChanged(Attribute attribute)
            throws IllegalActionException {
        if(attribute == inputMode) {
	/*    IntToken it = (IntToken) inputMode.getToken();
	    _inputMode = it.intValue();
            try {
                _createInputInterface();
            }
            catch(NameDuplicationException ex) {
                ex.printStackTrace(System.out);
            } */
        }
        else
        if(attribute == outputMode) {
            /*IntToken it = (IntToken) outputMode.getToken();
            _outputMode = it.intValue();
            try {
                _createOutputInterface();
            }
            catch(NameDuplicationException ex) {
                ex.printStackTrace(System.out);
            } */           
        }       
	else {
            super.attributeChanged(attribute);
        }        
    }
    
    public void initialize() throws IllegalActionException {
        super.initialize();
	
	_initSymbols = new InitSymbols();
	
	_symbol = new double[510];
	_lastsymbol = new double[510];
	_state = InitSymbols.R_ACT_REQ;
	_rxCounter = 0;
	_txCounter = 0;
	
	_currentFire = 0;
	_lastFire = 0;

        ((DEDirector) getDirector()).fireAt(this, 0);
    }
    
    public void fire() throws IllegalActionException {

        if(timerEvent.hasToken(0)) {
	    timerEvent.get(0);
	    _timerEvent = true;
	}
	else {
	    _timerEvent = false;
	}

        _getSymbol();

        if(reset.hasToken(0)) {
//	    System.out.println("ATUR: Reset detected ... " +
//	        "starting initialization");
	    reset.get(0);
	    _state = _initSymbols.R_ACT_REQ;
//	    System.out.println("\\\\\\ ATU-R : R_ACT_REQ ///");
	    idftMode.send(0, new BooleanToken(false));
	    dftMode.send(0, new BooleanToken(false));

	    _txCounter = 0;
	    _rxCounter = 0;
	    startTimer.send(0, new DoubleToken(248));
	}
	else {
            switch(_state) {
                case InitSymbols.R_ACT_REQ: {
		    
		    if(_hassymbol && _initSymbols.checkATUCSymbol(
		            InitSymbols.C_ACT2_S1, _symbol)) {
//                        System.out.println("ATUR-Initializer: got C_ACT2_S1");
		        _state = InitSymbols.MY_DETECT_C_ACT;
//        	        System.out.println("\\\\\\ ATU-R : MY_DETECT_C_ACT ///");
			_rxCounter = 1;
		        if(!_timerEvent) stopTimer.send(0, new Token());
		    }
                    else {
			_ret = _transmitSignal(InitSymbols.R_ACT_REQ_S1,
			    InitSymbols.R_ACT_REQ_S2);
//			System.out.println("ATUR-Initializer: transmitted " +
//			    "R_ACT_REQ");
			if(_ret == 1) {
			    _txCounter = 0;
			    startTimer.send(0, new DoubleToken(249 * 895));
//  		            System.out.println("ATUR-Initializer: " +
//			        "R_ACT_REQ -> waiting for 896 symbols");
			}
		    }

		} break;
		
		case InitSymbols.R_QUIET1: {
		
		} break;
		
		case InitSymbols.MY_DETECT_C_ACT: {
		    
		    _ret = _detectSignal(InitSymbols.C_ACT2_S1, 
		            InitSymbols.C_ACT2_S2);

		    if(_ret == 0) {
		        _rxCounter = 0;
			_state = InitSymbols.R_ACT_REQ;
//	                System.out.println("\\\\\\ ATU-R : R_ACT_REQ ///");			
			startTimer.send(0, new DoubleToken(249));
		    }
		    else
		    if(_ret == 2) {
			_state = InitSymbols.R_ACK;
//   	                System.out.println("\\\\\\ ATU-R : R_ACK ///");
                        startTimer.send(0, new DoubleToken(250));			
//			System.out.println("ATUR-Initializer: # Detected " + 
//			    "C_ACT2 #");
		    }
		    else {
//			System.out.println("ATUR-Initializer: Detecting " + 
//			    "C_ACT2");
		    }
		    
		} break;
		
		case InitSymbols.R_ACK: {
	            _ret = _transmitSignal(InitSymbols.R_ACK2_S1,
		            InitSymbols.R_ACK2_S2);
//		    System.out.println("ATUR-Initializer: transmitted " +
//		            "R_ACK2");

		    if(_ret == 1) {
	                _rxCounter = 0;
		        startTimer.send(0, new DoubleToken(250 * 4000 / _BASE));
			// _state = InitSymbols.R_QUIET2;
			_state = InitSymbols.SHOWTIME;
//	                System.out.println("\\\\\\ ATU-R : R_QUIET2 ///");
//			System.out.println(" $$$$$$$ ATUR : End of Activation " +
//			    " $$$$$$");
		    }

		} break;
		
		case InitSymbols.R_QUIET2: {

                    _ret = _detectSignal(InitSymbols.C_REVEILLE_S1,
		        InitSymbols.C_REVEILLE_S2);
		    
		    if(_ret == 0) {
			_state = InitSymbols.R_ACT_REQ;
//	                System.out.println("\\\\\\ ATU-R : R_ACT_REQ ///");
	                ((DEDirector) getDirector()).fireAt(this, _currentFire + 250);
                        _rxCounter = 0;
			if(!_timerEvent) stopTimer.send(0, new Token());
		    }
		    else
		    if(_ret == 2) {
		        _state = InitSymbols.MY_PART_C_PILOT1;
//	                System.out.println("\\\\\\ ATU-R : MY_PART_C_PILOT1 ///");
		        _rxCounter = 0;
//			System.out.println("ATUR: Got C_REVEILLE");
		    }
		    else
		    if(_ret == 1) {
		        if(_timerEvent) {
//			    System.out.println("ATUR # R_QUIET2 -> Time out");
			    _state = InitSymbols.R_ACT_REQ;
// 	                    System.out.println("\\\\\\ ATU-R : R_ACT_REQ ///");
	                    ((DEDirector) getDirector()).fireAt(this, _currentFire + 250);			    
			    _rxCounter = 0;
			}
			else {
//			    System.out.println("ATUR : Detecting C_REVEILLE");
			}
		    }
		
		} break;
		
		case InitSymbols.MY_PART_C_PILOT1: {
		
		    if(_timerEvent) {
//			System.out.println("ATUR # R_QUIET2 -> Time out");
			_state = InitSymbols.R_ACT_REQ;
//	                System.out.println("\\\\\\ ATU-R : R_ACT_REQ ///");			
	                ((DEDirector) getDirector()).fireAt(this, _currentFire + 250);
		    }
		    else
		    if(_hassymbol && _initSymbols.checkATUCSymbol(
		            InitSymbols.C_PILOT_S, _symbol)) {
		
		        _state = InitSymbols.R_REVERB1;
//	                System.out.println("\\\\\\ ATU-R : R_REVERB1 ///");
			if(!_timerEvent) stopTimer.send(0, new Token());
                        startTimer.send(0, new DoubleToken(1.0));
			// fireAt(_currentFire);
//			System.out.println("ATUR : Detected start of C_PILOT");
		    }
		
		} break;
		
		case InitSymbols.R_REVERB1: {

                    if(_timerEvent) {
                        if(_txCounter < 4096/_BASE) {
		            _sendSymbol(InitSymbols.R_REVERB_S);
			    startTimer.send(0, new DoubleToken(249));
			    _txCounter++;
//	                    System.out.println("ATUR-Initializer: transmitted "
//			        + "R_REVERB");
		        }
		        else {
		            _txCounter = 0;
			    _state = InitSymbols.R_QUIET3;
//	                    System.out.println("\\\\\\ ATU-R : R_QUIET3 ///");
			    startTimer.send(0, new DoubleToken(250 * 2048/_BASE));
//			    System.out.println("ATUR : Waiting in R_QUIET3");
		        }
		    }
		
		} break;
		
		case InitSymbols.R_QUIET3: {
		
		    if(_timerEvent) {
		        _state = InitSymbols.R_ECT;
//	                System.out.println("\\\\\\ ATU-R : R_ECT ///");
			startTimer.send(0, new DoubleToken(250 * 512 / _BASE));
//			System.out.println("ATUR : Waiting in R_ECT");			
		    }
		    
		} break;
		
		case InitSymbols.R_ECT: {
		
		    if(_timerEvent) {
		        _state = InitSymbols.R_REVERB2;
//	                System.out.println("\\\\\\ ATU-R : R_REVERB2 ///");
			startTimer.send(0, new DoubleToken(1.0));
			// fireAt(_currentFire);
		    }
		
		} break;
		
		case InitSymbols.R_REVERB2: {
		
		    if(_timerEvent) {
                        if(_txCounter < 1024/_BASE) {
		            _sendSymbol(InitSymbols.R_REVERB_S);
			    startTimer.send(0, new DoubleToken(249));
			    _txCounter++;
//	                    System.out.println("ATUR-Initializer: transmitted "
//			        + "R_REVERB2");
		        }
		        else {
		            _txCounter = 0;
			    _state = InitSymbols.SHOWTIME;
//	                    System.out.println("\\\\\\ ATU-R : SHOWTIME ///");
	                    ((DEDirector) getDirector()).fireAt(this, 
			        _currentFire + 1);
			    // fireAt(_currentFire);
//			    System.out.println(" $$$$$$ ATU-R : End of Transceiver "
//			        + " training $$$$$$");
		        }
		    }

		} break;

		case InitSymbols.SHOWTIME: {
		    start.send(0, new BooleanToken(true));
		    idftMode.send(0, new BooleanToken(true));
		    dftMode.send(0, new BooleanToken(true));
//		    System.out.println("ATUR-Initialization done ");
//  		    System.out.println("ATUR-Initializer: Showtime!");		    
		} break;
	    }
	}
    }   

    ///////////////////////////////////////////////////////////////////
    ////                         private methods                   ////

    private byte _detectSignal(int S1, int S2) throws
            IllegalActionException {

        if(_rxCounter == 0) {
	    if(_hassymbol) {
                if(_initSymbols.checkATUCSymbol(S1, _symbol)) {
		    _rxCounter++;
		}
		else {
		    _rxCounter = 0;
		    return 0;
                }
	    }
	    else {
	        _rxCounter = 0;
		return 0;
	    }
	}
        else
        if(_rxCounter < 64/_BASE) {
	    if(_hassymbol && ((_currentFire - _lastFire) == 250)) {
                if(_initSymbols.checkATUCSymbol(S1, _symbol)) {
		    _rxCounter++;
		}
		else {
		    _rxCounter = 0;
		    return 0;
                }
	    }
	    else {
	        _rxCounter = 0;
		return 0;
	    }
	}
	else
	if(_rxCounter < 128/_BASE) {
	    if(_hassymbol && ((_currentFire - _lastFire) == 250)) {
	        if(_initSymbols.checkATUCSymbol(S2, _symbol)) {
		    _rxCounter++;
		    if(_rxCounter == 128/_BASE) {
		        _rxCounter = 0;
		        return 2;
		    }
	        }
		else {
		    _rxCounter = 0;
		    return 0;
		}
	    }
	    else {
	        _rxCounter = 0;
		return 0;
	    }
	}
	
	return 1;
    }    

    private byte _transmitSignal(int S1, int S2) throws
            IllegalActionException {

        if(_txCounter < 64/_BASE) {
	    _sendSymbol(S1);
	    startTimer.send(0, new DoubleToken(249));
	    _txCounter++;
	}
        else
	if(_txCounter < 128/_BASE) {
	    _sendSymbol(S2);
	    _txCounter++;
	    if(_txCounter == 128/_BASE) return 1;
            startTimer.send(0, new DoubleToken(249));
	}

	return 0;
    }

    private void _getSymbol() throws IllegalActionException {

        _lastFire = _currentFire;
	_currentFire = ((DEDirector) getDirector()).getCurrentTime();

        if(fromATUC[0].hasToken(0)) {
	    for(int i = 0;i < _symbol.length;i++) {
                _lastsymbol[i] = _symbol[i];
	    }
	    if(_inputMode == 0) {
	        DoubleToken dt;
	        for(int i = 0;i < 255;i++) {
	            dt = (DoubleToken) fromATUC[i].get(0);
		    _symbol[2*i] = dt.doubleValue();

	            dt = (DoubleToken) fromATUC[i].get(0);
		    _symbol[2*i + 1] = dt.doubleValue();
	        }
	    }
	    else {
	        ObjectToken ot = (ObjectToken) fromATUC[0].get(0);
		_symbol = (double []) ot.getValue();
	    }
	    _hassymbol = true;
	}
	else {
	    for(int i = 0;i < _symbol.length;i++) {
                _lastsymbol[i] = _symbol[i];
	        _symbol[i] = 0;
	    }
	    _hassymbol = false;
	}
    }
    
    private void _sendSymbol(int symbol) throws IllegalActionException {
    
        double [] s = _initSymbols.getATURSymbol(symbol);
	if(_outputMode == 0) {
            for(int i = 0;i < 31;i++) {
	        toATUC[i].send(0, new DoubleToken(s[2*i]));
	        toATUC[i].send(0, new DoubleToken(s[2*i + 1]));
  	    }
	}
	else {
	    toATUC[0].send(0, new ObjectToken(s));
	}
    }

    private void _createInputInterface() throws
            IllegalActionException, NameDuplicationException { 	
	if(_inputMode == 0) {
	    fromATUC = new TypedIOPort[255];
	    for(int i = 0;i < 255;i++) {
	        fromATUC[i] = new TypedIOPort(this, "fromATUC_" + i, 
		        true, false);
	        fromATUC[i].setTypeEquals(BaseType.DOUBLE);
	    }
	}
	else
	if(_inputMode == 1) {
	    fromATUC = new TypedIOPort[1];
	    fromATUC[0] = new TypedIOPort(this, "fromATUC", true, false);
	    fromATUC[0].setTypeEquals(BaseType.OBJECT);
	}
	else {
	   throw new IllegalActionException(this, "Invalid fromATUCMode");	
	}
    }
	
    private void _createOutputInterface() throws
            IllegalActionException, NameDuplicationException {	
        if(_outputMode == 0) {
	    toATUC = new TypedIOPort[31];
	    for(int i = 0;i < 31;i++) {
	        toATUC[i] = new TypedIOPort(this, "toATUC_" + i, false, true);
	        toATUC[i].setTypeEquals(BaseType.DOUBLE);
	    }
	}
	else
	if(_outputMode == 1) {
	    toATUC = new TypedIOPort[1];
	    toATUC[0] = new TypedIOPort(this, "toATUC", false, true);
	    toATUC[0].setTypeEquals(BaseType.OBJECT);
	}
	else {
	   throw new IllegalActionException(this, "Invalid toATUCMode");
	}
    }

    ///////////////////////////////////////////////////////////////////
    ////                         private variables                 ////            

    private static final int _BASE = 32;

    private int _state;
    private byte _ret;
    private InitSymbols _initSymbols;
    private boolean _timerEvent;

    private int _rxCounter;
    private int _txCounter;
    
    private double [] _symbol;
    private double _currentFire;
    private double [] _lastsymbol;
    private double _lastFire;
    private boolean _hassymbol;

    private int _inputMode;
    private int _outputMode;
}
